#include <windows.h>
#include <vfw.h>
#include <stdio.h>

#include "Video.h"

PAVIFILE		g_pAviFile;
PAVISTREAM		g_pAviStream;
PAVISTREAM		g_pAviCompressedStream;

unsigned long	g_iBytesWritten;

/*
 =======================================================================================================================
 =======================================================================================================================
 */

int bAVIError(HRESULT _iResult)
{
	if(_iResult)
	{
		/*~~~~~~~~~~~~~~~~~*/
		char	*szError = 0;
		/*~~~~~~~~~~~~~~~~~*/

		switch(_iResult)
		{
		case AVIERR_UNSUPPORTED:	szError = "AVIERR_UNSUPPORTED"; break;
		case AVIERR_BADFORMAT:		szError = "AVIERR_BADFORMAT"; break;
		case AVIERR_MEMORY:			szError = "AVIERR_MEMORY"; break;
		case AVIERR_INTERNAL:		szError = "AVIERR_INTERNAL"; break;
		case AVIERR_BADFLAGS:		szError = "AVIERR_BADFLAGS"; break;
		case AVIERR_BADPARAM:		szError = "AVIERR_BADPARAM"; break;
		case AVIERR_BADSIZE:		szError = "AVIERR_BADSIZE"; break;
		case AVIERR_BADHANDLE:		szError = "AVIERR_BADHANDLE"; break;
		case AVIERR_FILEREAD:		szError = "AVIERR_FILEREAD"; break;
		case AVIERR_FILEWRITE:		szError = "AVIERR_FILEWRITE"; break;
		case AVIERR_FILEOPEN:		szError = "AVIERR_FILEOPEN"; break;
		case AVIERR_COMPRESSOR:		szError = "AVIERR_COMPRESSOR"; break;
		case AVIERR_NOCOMPRESSOR:	szError = "AVIERR_NOCOMPRESSOR"; break;
		case AVIERR_READONLY:		szError = "AVIERR_READONLY"; break;
		case AVIERR_NODATA:			szError = "AVIERR_NODATA"; break;
		case AVIERR_BUFFERTOOSMALL: szError = "AVIERR_BUFFERTOOSMALL"; break;
		case AVIERR_CANTCOMPRESS:	szError = "AVIERR_CANTCOMPRESS"; break;
		case AVIERR_USERABORT:		szError = "AVIERR_USERABORT"; break;
		case AVIERR_ERROR:			szError = "AVIERR_ERROR"; break;
		}

		printf("\nAVI Error : %s\n", szError);
		vStopRecording();
		return 1;
	}
	else
		return 0;
}

BITMAPINFO	g_oDstBitmapInfo = { { sizeof(BITMAPINFO), 0, 0, 1, 32, BI_RGB, 0, 72, 72, 0, 0 } };

/*
 =======================================================================================================================
 =======================================================================================================================
 */

int bStartRecording(int _iW, int _iH, int _iRefreshRate, char *_szFileName, char *_acCodec, int _bConfig)
{
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	AVICOMPRESSOPTIONS	oAviCompressOptions;
	AVISTREAMINFO		oAviStreamInfo;
	DWORD				iFCCHandler;
	COMPVARS			oCompvars;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

	iFCCHandler = mmioFOURCC(_acCodec[0], _acCodec[1], _acCodec[2], _acCodec[3]);
	g_oDstBitmapInfo.bmiHeader.biWidth = _iW;
	g_oDstBitmapInfo.bmiHeader.biHeight = _iH;
	g_oDstBitmapInfo.bmiHeader.biSizeImage = _iW * _iH * g_oDstBitmapInfo.bmiHeader.biBitCount / 8;

	if(_bConfig)
	{
		ZeroMemory(&oCompvars, sizeof(COMPVARS));
		oCompvars.cbSize = sizeof(COMPVARS);
		oCompvars.fccHandler = iFCCHandler;
		oCompvars.fccType = ICTYPE_VIDEO;
		oCompvars.dwFlags = ICMF_COMPVARS_VALID;
		if
		(
			ICCompressorChoose
				(
					0,
					ICMF_CHOOSE_DATARATE | ICMF_CHOOSE_KEYFRAME,
					&g_oDstBitmapInfo,
					0,
					&oCompvars,
					0
				) == FALSE
		) return 0;
		iFCCHandler = oCompvars.fccHandler;
		ICCompressorFree(&oCompvars);
	}

	AVIFileInit();

	remove(_szFileName);
	if(bAVIError(AVIFileOpen(&g_pAviFile, _szFileName, OF_CREATE | OF_WRITE, NULL))) return 0;

	ZeroMemory(&oAviStreamInfo, sizeof(AVISTREAMINFO));
	oAviStreamInfo.fccType = streamtypeVIDEO;
	oAviStreamInfo.fccHandler = iFCCHandler;
	oAviStreamInfo.dwScale = 1;
	oAviStreamInfo.dwRate = (DWORD) _iRefreshRate;
	if(_bConfig)
		oAviStreamInfo.dwQuality = oCompvars.lQ;
	else
		oAviStreamInfo.dwQuality = -1;
	oAviStreamInfo.dwSuggestedBufferSize = 0;
	SetRect(&oAviStreamInfo.rcFrame, 0, 0, _iW, _iH);
	strcpy(oAviStreamInfo.szName, "MNG");
	if(bAVIError(AVIFileCreateStream(g_pAviFile, &g_pAviStream, &oAviStreamInfo))) return 0;

	ZeroMemory(&oAviCompressOptions, sizeof(AVICOMPRESSOPTIONS));
	oAviCompressOptions.fccType = streamtypeVIDEO;
	oAviCompressOptions.fccHandler = oAviStreamInfo.fccHandler;
	oAviCompressOptions.dwQuality = oAviStreamInfo.dwQuality;
	oAviCompressOptions.lpFormat = &g_oDstBitmapInfo;
	oAviCompressOptions.cbFormat = g_oDstBitmapInfo.bmiHeader.biSize;
	if(_bConfig)
	{
		oAviCompressOptions.dwKeyFrameEvery = oCompvars.lKey;
		oAviCompressOptions.dwBytesPerSecond = oCompvars.lDataRate;
		oAviCompressOptions.dwFlags = AVICOMPRESSF_DATARATE | AVICOMPRESSF_KEYFRAMES;
	}
	else
		oAviCompressOptions.dwFlags = 0;

	if(bAVIError(AVIMakeCompressedStream(&g_pAviCompressedStream, g_pAviStream, &oAviCompressOptions, NULL)))
		return 0;

	if
	(
		bAVIError
			(
				AVIStreamSetFormat
					(
						g_pAviCompressedStream,
						0,
						(LPVOID) & g_oDstBitmapInfo,
						g_oDstBitmapInfo.bmiHeader.biSize
					)
			)
	) return 0;

	g_iBytesWritten = 0;
	return 1;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
int bRecord(void *_pvVideoBits, unsigned int _iSample)
{
	/*~~~~~~~~~~~~~~~~~~*/
	long	iBytesWritten;
	int		iResult;
	/*~~~~~~~~~~~~~~~~~~*/

	iResult = !bAVIError
		(
			AVIStreamWrite
			(
				g_pAviCompressedStream,
				_iSample,
				1,
				_pvVideoBits,
				g_oDstBitmapInfo.bmiHeader.biSizeImage,
				0,
				NULL,
				&iBytesWritten
			)
		);

	g_iBytesWritten += iBytesWritten;

	return iResult;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void vStopRecording(void)
{
	if(g_pAviCompressedStream)
	{
		AVIStreamRelease(g_pAviCompressedStream);
		g_pAviCompressedStream = NULL;
	}

	if(g_pAviStream)
	{
		AVIStreamRelease(g_pAviStream);
		g_pAviStream = NULL;
	}

	if(g_pAviFile)
	{
		AVIFileRelease(g_pAviFile);
		g_pAviFile = NULL;
	}

	AVIFileExit();
}
